package cn.com.framework.template.lib;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.com.commons.utils.StringUtil;
import cn.com.support.utils.ApplictionContextUtil;
import freemarker.core.Environment;
import freemarker.ext.servlet.HttpRequestHashModel;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;

/**
 * 通用模板标签
 * 
 * @author Microbun
 * 
 */
public class IncludeCommonsTagDirective implements TemplateDirectiveModel {

	Logger logger = LoggerFactory.getLogger(IncludeCommonsTagDirective.class);

	private String templatePath = null;

	private final static String EXECUTE_NAME = "execute";

	private final static String PARAM_PATH = "path";

	@SuppressWarnings({ "unused", "rawtypes" })
	private Class clazz;

	@SuppressWarnings("rawtypes")
	@Override
	public void execute(Environment env, Map params, TemplateModel[] loopVars,
			TemplateDirectiveBody body) throws TemplateException, IOException {
		if (params.containsKey(PARAM_PATH)) {
			SimpleScalar pathScalar = (SimpleScalar) params.get(PARAM_PATH);
			String path = StringUtil.convertSlashToDot(StringUtil
					.removeSuffix(pathScalar.getAsString()));
			String className = convertTemplatePathToClassName(path);
			Class clazz = null;
			try {
				clazz = Class.forName(className);
				HttpRequestHashModel requestHashModel = ((HttpRequestHashModel) (env
						.getGlobalVariable("Request")));
				invoke(clazz, requestHashModel);
			} catch (ClassNotFoundException e) {
				// 忽略
			}
		}
	}

	/**
	 * 转化模板路径为类名
	 * 
	 * @param templatePath
	 * @return
	 */
	private String convertTemplatePathToClassName(String path) {
		String className = null;
		String pathName = null;
		className = path.substring(path.lastIndexOf(".") + 1,
				path.length());
		pathName = path.substring(0, path.lastIndexOf(".") + 1);
		className = (className.charAt(0) + "").toUpperCase()
				+ className.substring(1);
		className = templatePath + pathName + className;
		return className;
	}

	/**
	 * 调用模板类
	 * 
	 * @param clazz
	 * @param requestHashModel
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void invoke(Class clazz, HttpRequestHashModel requestHashModel) {
		Object obj = ApplictionContextUtil.getApplicationContext().getBean(
				clazz);
		// 搜索可执行方法
		Method[] methodes = clazz.getDeclaredMethods();
		int methodCount = methodes.length;
		Method execute = null;
		int foundExecute = 0;
		for (int i = 0; i < methodCount; i++) {
			if (EXECUTE_NAME.equals(methodes[i].getName())) {
				execute = methodes[i];
				foundExecute++;
			}
			if (foundExecute > 1) {
				throw new FrameworkTemplateException("[" + clazz.getName()
						+ "]中存在多个" + EXECUTE_NAME + "()方法，无法进行匹配,请确认保留一个。");
			}
		}
		// if (foundExecute == 0) {
		// throw new FrameworkTemplateException("[" + clazz.getName()
		// + "]中至少需要一个" + EXECUTE_NAME + "()方法");
		// }

		// 拼配并传递参数
		if (execute == null) {
			return;
		}
		HttpServletRequest request = requestHashModel.getRequest();
		HttpServletResponse response = requestHashModel.getResponse();
		HttpSession session = request.getSession();
		Class[] executeParamsType = execute.getParameterTypes();
		ArrayList<Object> args = new ArrayList<Object>();
		int executeParamsCount = executeParamsType.length;
		for (int i = 0; i < executeParamsCount; i++) {
			if (executeParamsType[i].isAssignableFrom(HttpServletRequest.class)) {
				args.add(request);
			} else if (executeParamsType[i]
					.isAssignableFrom(HttpServletResponse.class)) {
				args.add(response);
			} else if (executeParamsType[i]
					.isAssignableFrom(HttpSession.class)) {
				args.add(session);
			} else {
				throw new FrameworkTemplateException("[" + clazz.getName()
						+ "." + EXECUTE_NAME + "]无法接受参数["
						+ executeParamsType[i] + "]");
			}
		}

		try {
			execute.invoke(obj, args.toArray());
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 模板包路径
	 * 
	 * @param templatePath
	 */
	public void setTemplatePath(String templatePath) {
		if (templatePath != null) {
			this.templatePath = templatePath;
		} else {
			throw new FrameworkTemplateException("templatePath 已经被赋值，不能重新赋值");
		}
	}

}
